package org.budo.launcher.service.util;

import java.io.File;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.security.CodeSource;
import java.util.Properties;

import org.budo.support.java.net.util.JavaNetUrlUtil;
import org.budo.support.java.net.util.JavaNetUtil;
import org.budo.support.lang.util.FileUtil;
import org.budo.support.lang.util.NumberUtil;
import org.budo.support.lang.util.PropertiesUtil;
import org.budo.support.lang.util.SystemUtil;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author lmw
 */
public class LauncherUtil {
    private static final Logger log = Slf4j.getLogger();

    private static final String[] COMMON_DOC_ROOTS = { "src/main/webapp" };

    private static final String PORT_KEY = "budo.port";

    public static File getCodeSourceArchive() {
        CodeSource codeSource = LauncherUtil.class.getProtectionDomain().getCodeSource();
        return getCodeSourceArchive(codeSource);
    }

    public static File getCodeSourceArchive(CodeSource codeSource) {
        try {
            URL location = (codeSource == null ? null : codeSource.getLocation());
            if (location == null) {
                log.error("#33 getCodeSourceArchive return null, codeSource=" + codeSource);
                return null;
            }

            String path;
            URLConnection connection = location.openConnection();
            if (connection instanceof JarURLConnection) {
                path = ((JarURLConnection) connection).getJarFile().getName();
            } else {
                path = location.toURI().getPath();
            }

            if (path.contains("!/")) {
                path = path.substring(0, path.indexOf("!/"));
            }

            return new File(path);
        } catch (Exception ex) {
            return null;
        }
    }

    private static File getArchiveFileDocumentRoot(String extension) {
        File codeSourceArchive = getCodeSourceArchive();
        if (codeSourceArchive != null //
                && codeSourceArchive.exists() //
                && !codeSourceArchive.isDirectory() //
                && codeSourceArchive.getName().toLowerCase().endsWith(extension)) {
            File absoluteFile = codeSourceArchive.getAbsoluteFile();
            log.warn("#60 getArchiveFileDocumentRoot return " + absoluteFile + ", codeSourceArchive=" + codeSourceArchive);
            return absoluteFile;
        }

        log.warn("#68 getArchiveFileDocumentRoot return null, codeSourceArchive=" + codeSourceArchive);
        return null;
    }

    private static File getCommonDocumentRoot() {
        for (String commonDocRoot : COMMON_DOC_ROOTS) {
            File root = new File(commonDocRoot);

            if (root.exists() && root.isDirectory()) {
                return root.getAbsoluteFile();
            }
        }

        return null;
    }

    public final static File getDocumentRoot() {
        File file = getWarFileDocumentRoot(); // 本地开发环境为空
        if (null != file) {
            log.info("#87 getDocumentRoot, getWarFileDocumentRoot=" + file);
            return file;
        }

        file = getExplodedWarFileDocumentRoot(); // 本地开发环境为空
        if (null != file) {
            log.info("#93 getDocumentRoot, getExplodedWarFileDocumentRoot=" + file);
            return file;
        }

        file = getCommonDocumentRoot(); // 本地开发环境为 webapp 目录
        log.info("#98 getDocumentRoot, getCommonDocumentRoot=" + file);
        return file;
    }

    private static File getExplodedWarFileDocumentRoot() {
        File codeSourceArchive = getCodeSourceArchive();
        return getExplodedWarFileDocumentRoot(codeSourceArchive);
    }

    private static File getExplodedWarFileDocumentRoot(File codeSourceFile) {
        log.info("#120 getExplodedWarFileDocumentRoot, codeSourceFile=" + codeSourceFile);

        if (codeSourceFile != null && codeSourceFile.exists()) {
            String path = codeSourceFile.getAbsolutePath();
            int webInfPathIndex = path.indexOf(File.separatorChar + "WEB-INF" + File.separatorChar);
            if (webInfPathIndex >= 0) {
                path = path.substring(0, webInfPathIndex);
                return new File(path);
            }
        }
        return null;
    }

    private static File getWarFileDocumentRoot() {
        return getArchiveFileDocumentRoot(".war");
    }

    public static Integer getPort() {
        Integer value = SystemUtil.getInteger(PORT_KEY);

        if (null != value) {
            return value;
        }

        return getPortFromBonf();
    }

    private static Integer getPortFromBonf() {
        URL conf = Thread.currentThread().getContextClassLoader().getResource("automan.conf");
        if (null == conf) {
            return null;
        }

        InputStream openStream = JavaNetUrlUtil.openStream(conf);
        Properties properties = PropertiesUtil.load(openStream);
        String key = "automan.AUTOMAN_PORT";
        String value = properties.getProperty(key);
        if (null == value) {
            return null;
        }

        Integer port = NumberUtil.toInteger(value.trim());

        // 自动选择端口
        if (port < 0) { // 如果是负数
            for (int i = -port;; i++) { // 就以其正数开始找可用的
                if (!JavaNetUtil.isLoclePortUsing(i)) {
                    log.warn("#159 budo.launcher.port.auto = " + i);
                    return i;
                }
            }
        }

        return port;
    }

    public static File createTempDir(String tempPrefix, Integer port) {
        try {
            String prefix = tempPrefix + ".";
            String suffix = "." + port;
            File tempDir = FileUtil.createTempFile(prefix, suffix);

            tempDir.delete();
            tempDir.mkdir();
            tempDir.deleteOnExit();

            log.info("#140 tempDir = " + tempDir);
            return tempDir;
        } catch (Throwable ex) {
            throw new RuntimeException("#143 Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
        }
    }

    public static File getBudoWebAppDir() {
        File jarFile = LauncherUtil.getCodeSourceArchive();
        log.info("#89 getBudoWebAppDir, maybe jarFile is " + jarFile);

        if (jarFile == null || !jarFile.exists() || jarFile.isDirectory() || !jarFile.getName().toLowerCase().endsWith(".jar")) {
            log.warn("#92 getBudoWebAppDir, return, jarFile not ok, maybe not in jar, " + jarFile);
            return null;
        }
        log.info("#96 getBudoWebAppDir, jarFile is " + jarFile);

        File libDir = jarFile.getParentFile();
        if (null == libDir || !libDir.exists() || !libDir.isDirectory()) {
            log.warn("#92 getBudoWebAppDir, return, libDir not ok, " + libDir);
            return null;
        }
        log.info("#103 getBudoWebAppDir, libDir is " + libDir);

        File launcherDir = libDir.getParentFile();
        if (null == launcherDir || !launcherDir.exists() || !launcherDir.isDirectory()) {
            log.warn("#104 getBudoWebAppDir, return, launcherDir not ok, " + launcherDir);
            return null;
        }
        log.info("#110 getBudoWebAppDir, launcherDir is " + launcherDir);

        File webappDir = new File(launcherDir, "webapp");
        if (!webappDir.exists() || !webappDir.isDirectory()) {
            log.warn("#110 getBudoWebAppDir, return, webappDir not ok, " + webappDir);
            return null;
        }
        log.info("#117 getBudoWebAppDir, webappDir is " + webappDir);

        File webAppDir = FileUtil.getCanonicalFile(webappDir);
        return webAppDir;
    }
}